/**
 * TetisDB
 *
 * Classe para efetuar a decodificacao do hash em objeto
 */

package br.com.tetisdb.io.snapShot.decoder;

import br.com.tetisdb.io.snapShot.*;
import br.com.tetisdb.lang.TetisObject;
import br.com.tetisdb.util.TetisUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;

import br.com.tetisdb.lang.TetisHashMap;
import java.util.Iterator;
import java.util.List;

public class Decoder extends TetisObject {
    //snapShot
    private static TetisHashMap<String, Object> snap;
    
    /**
     * Construtor Padrao
     *
     * @param snap SnapShot
     */
    public Decoder(TetisHashMap<String, Object> snap) {
        this.snap = snap;
    }
    
    /**
     * Metodo que efetua a decodificacao
     *
     * @param hash Objeto codificado
     * @return Object Objeto decodificado
     */
    public Object decode(TetisHashMap<String, Object> hash) {
        try {
            tetisLogger.debug("Decodificando objeto.");
            
            String sNome = (String) hash.get(SnapShotManager.NOMECLASSE);
            Object object = Class.forName(sNome).newInstance();
            Field[] atributos = TetisUtil.getFields(object.getClass()).toArray(new Field[0]);
            String[] tipos = ((String) hash.get(SnapShotManager.TIPOSATRIBUTOS)).split("\\" + SnapShotManager.PIPE);
            
            for (int i=0; i<atributos.length; i++) {
                Field atr = (Field) atributos[i];
                String tipo = (String) tipos[i];
                
                if((atr != null) && (TetisUtil.stringIsValid(tipo))) {
                    if(!Modifier.isFinal(atr.getModifiers())) { //nao pode alterar tipos final
                        String[] atrClazz = ((String) hash.get(SnapShotManager.ATRIBUTOS)).split("\\" + SnapShotManager.PIPE);
                        Arrays.sort(atrClazz);
                        
                        // garante que o atributo existe no objeto do hash
                        if ((atr != null) && (Arrays.binarySearch(atrClazz, atr.getName()) >= 0)) {
                            if (!atr.isAccessible()) {
                                atr.setAccessible(true);
                            }
                            
                            String valor = (String) hash.get(atr.getName());
                            String sTipo = tipo.trim();
                            
                            if (atr.getType().isPrimitive()) {
                                atr = tipoPrimitivo(atr, valor, sTipo, object);
                            } else {
                                atr = tipoObjeto(atr, valor, sTipo, object);
                            }
                        }
                    }
                }
            }
            
            tetisLogger.debug("Objeto decodificado: "+object);
            
            return object;
        } catch (Exception e) {
            tetisLogger.error("Erro ao decodificar objeto. Erro: "+e.getMessage());
            return null;
        }
    }
    
    private Field tipoObjeto(Field atr, String valor, String sTipo, Object object) throws Exception {
        if (sTipo.startsWith("java.lang")) {
            if (sTipo.equalsIgnoreCase("java.lang.String")) {
                atr.set(object, new String(valor));
            }else if (sTipo.equalsIgnoreCase("java.lang.Double")) {
                atr.set(object, new Double(valor));
            } else if (sTipo.equalsIgnoreCase("java.lang.Integer")) {
                atr.set(object, new Integer(valor));
            } else if (sTipo.equalsIgnoreCase("java.lang.Float")) {
                atr.set(object, new Float(valor));
            } else if (sTipo.equalsIgnoreCase("java.lang.Long")) {
                atr.set(object, new Long(valor));
            } else if (sTipo.equalsIgnoreCase("java.lang.Boolean")) {
                atr.set(object, new Boolean(valor));
            } else if (sTipo.equalsIgnoreCase("java.lang.Byte")) {
                atr.set(object, new Byte(valor));
            } else if (sTipo.equalsIgnoreCase("java.lang.Short")) {
                atr.set(object, new Short(valor));
            } else {
                atr.set(object, null);
            }
            
        } else {
            Object o = null;
            
            if(!valor.equalsIgnoreCase("null")) {
                o = decode((TetisHashMap<String, Object>) snap.get(valor));
            }
            
            atr.set(object, o);
        }
        
        return atr;
    }
    
    private Field tipoPrimitivo(Field atr, String valor, String sTipo,Object object) throws Exception {
        if (sTipo.equalsIgnoreCase("int")) {
            atr.setInt(object, Integer.parseInt(valor));
        } else if (sTipo.equalsIgnoreCase("float")) {
            atr.setFloat(object, Float.parseFloat(valor));
        } else if (sTipo.equalsIgnoreCase("double")) {
            atr.setDouble(object, Double.parseDouble(valor));
        } else if (sTipo.equalsIgnoreCase("boolean")) {
            atr.setBoolean(object, Boolean.parseBoolean(valor));
        } else if (sTipo.equalsIgnoreCase("byte")) {
            atr.setByte(object, Byte.parseByte(valor));
        } else if (sTipo.equalsIgnoreCase("char")) {
            atr.setChar(object, valor.charAt(0));
        } else if (sTipo.equalsIgnoreCase("long")) {
            atr.setLong(object, Long.parseLong(valor));
        } else if (sTipo.equalsIgnoreCase("short")) {
            atr.setShort(object, Short.parseShort(valor));
        }
        
        return atr;
    }
}
